package cn.qidd.keep.domain.web;

import cn.qidd.keep.domain.core.Constants;
import cn.qidd.keep.domain.core.dict.SuccessOrFailure;
import cn.qidd.keep.domain.model.OperationLogModel;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * Created by sunyanping on 2019/3/5
 */
@Component
@Aspect
public class OperationLogAspect {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RestTemplate restTemplate;

    private ThreadLocal<Long> startTimestamp = new ThreadLocal<>();

    @Pointcut(value = "execution(public * cn.qidd.keep.*.controller.*.*.*(..))")
    public void recordLog() {
    }

    @Before("recordLog()")
    public void before() {
        startTimestamp.set(System.currentTimeMillis());
    }

    @AfterReturning("recordLog()")
    public void afterRunning(JoinPoint joinPoint) {
        addOperationLogRecord(joinPoint, null);
    }

    @AfterThrowing(pointcut = "recordLog()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Throwable e) {
        addOperationLogRecord(joinPoint, e);
    }

    private void addOperationLogRecord(JoinPoint joinPoint, Throwable e) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return;
        }
        HttpServletRequest request = requestAttributes.getRequest();
        Long nowTimestamp = System.currentTimeMillis();
        Long aLong = startTimestamp.get();
        //记录执行请求耗时
        String token = request.getHeader(Constants.TOKEN);
        if (StringUtils.isBlank(token)) {
            return;
        }
        String tokenString = stringRedisTemplate.opsForValue().get(token);
        UserVO userVO = JSONObject.parseObject(tokenString, UserVO.class);
        if (userVO == null) {
            return;
        }
        OperationLogModel operationLog = new OperationLogModel();
        operationLog.setRequestURL(request.getRequestURI());
        operationLog.setRequestMethod(request.getMethod());
        operationLog.setExecuteTime(nowTimestamp - aLong);
        operationLog.setStatus(e == null ? SuccessOrFailure.SUCCESS : SuccessOrFailure.FAILURE);
        operationLog.setSignature(joinPoint.getSignature().toLongString());
        operationLog.setDescription(getOperationShortLog(joinPoint));
        operationLog.setText(getStackTrace(e));
        operationLog.setOperatorTime(new Date());
        operationLog.setOperatorName(userVO.getUserName());
        operationLog.setOperatorRealName(userVO.getRealName());
//        new Thread(() -> {
//            restTemplate.postForEntity("http://service-manage/api/operation-log/addOperationLog", operationLog, Void.class);
//        }).start();

    }

    private String getOperationShortLog(JoinPoint joinPoint) {
        String text = "";
        Class targetClass = joinPoint.getSignature().getDeclaringType();
        Method[] methods = targetClass.getMethods();
        for (Method method : methods) {
            if (StringUtils.equals(method.toString(), joinPoint.getSignature().toLongString())) {
                ApiOperation annotation = method.getAnnotation(ApiOperation.class);
                if (annotation != null) {
                    text = annotation.value();
                }
            }
        }
        return text;
    }

    private String getStackTrace(Throwable e) {
        if (e == null) {
            return null;
        }
        StackTraceElement[] stackTrace = e.getStackTrace();
        return StringUtils.join(stackTrace, "\n");
    }
}
